package com.jy.modules.cims.component.sysAssist;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.Date;
import java.util.List;
import java.util.UUID;

import jodd.util.StringUtil;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSON;
import com.jy.modules.cims.common.CimsConstant;
import com.jy.modules.cims.common.exception.CimsException;
import com.jy.modules.cims.common.exception.CimsExceptionConstant;
import com.jy.modules.cims.common.exception.SysAssistException;
import com.jy.modules.cims.common.sysUtils.ExceptionUtils;
import com.jy.modules.cims.common.sysUtils.ReflectUtils;
import com.jy.modules.cims.common.sysUtils.SearchParamUtil;
import com.jy.modules.cims.common.sysUtils.SpringContextUtils;
import com.jy.modules.cims.data.dao.DueExecuteTaskDao;
import com.jy.modules.cims.data.dao.InteractClassParamterDao;
import com.jy.modules.cims.data.dto.DueExecuteTaskCfgDto;
import com.jy.modules.cims.data.dto.InteractClassParamterDto;
import com.jy.modules.cims.interact.InteractClient;

@Service("com.jy.modules.cims.component.sysAssist.DueExecuteTaskManager")
public class DueExecuteTaskManager extends AbstractDueExecuteTaskManager {
	
	private static final Logger logger = LoggerFactory.getLogger(InteractClient.class);
	
	@Autowired
	public DueExecuteTaskDao dueExecuteTaskDao;
	
	@Autowired
    public InteractClassParamterDao interactClassParamterDao;
	
	@Autowired
    public SpringContextUtils springContextUtil;
	
	private boolean validDueExecuteTaskParams(String taskMethod,
		       Date dueDate, Object... objects) {
	
		String taskInstance = CimsConstant.DUE_EXECUTE_TASK_BEAN;
		
		if(StringUtil.isBlank(taskMethod))
			throw new SysAssistException(CimsExceptionConstant.SYSASSIST_DUETASK_TASKMETHOD_ERROR);
		
		if(dueDate == null)
			throw new SysAssistException(CimsExceptionConstant.SYSASSIST_DUETASK_DUEDATE_ERROR);
		
		Object bean = null;
		try {
			bean = springContextUtil.getBean(taskInstance);
		} catch(Exception e) {
			throw new SysAssistException(MessageFormat.format(CimsExceptionConstant.SPRING_REFLECT_BEAN_NOT_FOUND, new Object[] { taskInstance }));
		}
		
		Class<?>[] parameterTypes = null;
		if (null != objects) {
			parameterTypes = new Class[objects.length];
			for (int index = 0; index < objects.length ; index++) {
				
				if (objects[index].getClass().getName().equals(java.util.ArrayList.class.getName())) {
					parameterTypes[index] = java.util.List.class;
				} else {
					parameterTypes[index] = objects[index].getClass();
				}
			}
		}
		
		try {
			Method method = bean.getClass().getMethod(taskMethod, parameterTypes);
		} catch(NoSuchMethodException e) {
			throw new SysAssistException(MessageFormat.format(CimsExceptionConstant.JAVA_REFLECT_METHOD_NOT_FOUND,
					new Object[] { bean.getClass().getName(), taskMethod }));
		}
		
		/*if(dueDate.compareTo(new Date()) < 1) {
			throw new SysAssistException(CimsExceptionConstant.SYSASSIST_DUETASK_DUEDATE_BEFORE_CURRENT);
		}*/	
		
		return true;
	}
	
	@Override
	public String addDueExecuteTask(String taskMethod, Date dueDate,
			Object... objects) {
		String taskId = UUID.randomUUID().toString().replaceAll("-", "");
		if(!validDueExecuteTaskParams(taskMethod,
				dueDate,  objects))
			return null;
		DueExecuteTaskCfgDto dto = new DueExecuteTaskCfgDto();
		dto.setTaskid(taskId);
		dto.setMethod(taskMethod);
		dto.setDuedate(new java.sql.Timestamp(dueDate.getTime()));
		dto.setDuestatus(CimsConstant.INTERACT_IMPL_FALSE);
		dto.setFailnum(0L);
		dueExecuteTaskDao.insertDueExecuteTaskCfg(SearchParamUtil.getSearchParam(dto));
		
		for(Object object : objects) {
			InteractClassParamterDto interactClassParamterDto = new InteractClassParamterDto();
			interactClassParamterDto.setBusinessCode(taskId);

			String parameterName = object.getClass().getName();
			if (ReflectUtils.isInterface(object.getClass(), java.util.Collection.class.getName())) {
				@SuppressWarnings("rawtypes")
				java.util.Collection collection = (java.util.Collection) object;
				parameterName = parameterName + ";" + collection.iterator().next().getClass().getName();
			}
			interactClassParamterDto.setParameterName(parameterName);
			
			interactClassParamterDto.setParameterValue(JSON.toJSON(object).toString());
			interactClassParamterDao.insertInteractClassParamter(SearchParamUtil.getSearchParam(interactClassParamterDto));
		}
		
		return taskId;
	}

	/**
	 * 真正触发到期任务执行
	 * dueExecuteTaskDto 中的任务默认已经满足执行条件，验证任务执行条件的逻辑不应写到这里
	 * @param dueExecuteTaskDto 
	 * @return void
	 * @author chenguixue
	 * @date 2015-3-27-下午2:46:43
	 * @update
	 */
	private void runDueExecuteTaskTrue(DueExecuteTaskCfgDto dueExecuteTaskDto) {
		String taskInstance = CimsConstant.DUE_EXECUTE_TASK_BEAN;
		Object bean = null;
		try {
			bean = springContextUtil.getBean(taskInstance);
		} catch(Exception e) {
			throw new SysAssistException(MessageFormat.format(CimsExceptionConstant.SPRING_REFLECT_BEAN_NOT_FOUND, new Object[] { taskInstance }));
		}
		
		String className = bean.getClass().getName();
		String methodName = dueExecuteTaskDto.getMethod();
		
		InteractClassParamterDto interactClassParamterDTO = new InteractClassParamterDto();
		interactClassParamterDTO.setBusinessCode(dueExecuteTaskDto.getTaskid());
		List<InteractClassParamterDto> paramList = interactClassParamterDao
				.searchInteractClassParamter(SearchParamUtil.getSearchParam(interactClassParamterDTO));
		
		Class<?>[] parameterTypes = null;
		Object[] args = null;
		// 转化参数的类，json转化为java对应类型值
		if (null != paramList) {
			parameterTypes = new Class[paramList.size()];
			args = new Object[paramList.size()];

			for (int index = 0; index < paramList.size(); index++) {
				InteractClassParamterDto interactClassParamterDto = paramList.get(index);
				String[] parameterName = interactClassParamterDto.getParameterName().split(";");
				String parameterValue = interactClassParamterDto.getParameterValue();
				
				Class<?> parameterClass = null;
				try {
					parameterClass = Class.forName(parameterName[0]);
				} catch (Exception e) {
					throw new CimsException(MessageFormat.format(CimsExceptionConstant.JAVA_REFLECT_METHOD_CLASS_NOT_FOUND,
							new Object[] { parameterName[0] }));
				}

	
				if(parameterName[0].equals(java.util.ArrayList.class.getName())) {
					parameterTypes[index] = java.util.List.class;
				} else {
					parameterTypes[index] = parameterClass;
				}
				if (String.class.getName().equals(parameterName[0])) {
					args[index] = parameterValue;
				} else {
					if (ReflectUtils.isInterface(parameterClass, java.util.Collection.class.getName())) {
						try {
							args[index] = JSON.parseArray(parameterValue, Class.forName(parameterName[1]));
						} catch (Exception e) {
							throw new CimsException(MessageFormat.format(CimsExceptionConstant.JAVA_REFLECT_METHOD_CLASS_NOT_FOUND,
									new Object[] { parameterName[1] }));
						}
					} else {
						args[index] = JSON.parseObject(parameterValue, parameterClass);
					}
				}
			}
		}
		
		Method method = null;
		try {
			method = Class.forName(className).getMethod(methodName, parameterTypes);
		}catch (Exception e) {
			throw new CimsException(MessageFormat.format(CimsExceptionConstant.JAVA_REFLECT_METHOD_NOT_FOUND,
					new Object[] { taskInstance, methodName }));
		}

		try {
			method.invoke(bean, args);
		} catch (InvocationTargetException e) {
		    Throwable throwable = e.getTargetException();// 获取目标异常  
		   
			throw new CimsException(throwable.getMessage());
		} catch (Exception e) {
		
			logger.error("到期执行错误2" ,ExceptionUtils.getStackTraceAsString(e));
			throw new CimsException(MessageFormat.format(CimsExceptionConstant.JAVA_REFLECT_METHOD_INVOKE_FAIL,
					new Object[] { taskInstance, methodName }));
		}
		
		
		
	}
	
	/**
	 * 执行到期任务前处理
	 * @param taskId 
	 * @return void
	 * @author chenguixue
	 * @date 2015-3-27-下午5:24:33
	 * @update
	 */
	private void runDueExecuteTaskBefore(String taskId) {
		DueExecuteTaskCfgDto dto = new DueExecuteTaskCfgDto();
		dto.setTaskid(taskId);
		dto.setDuestatus(CimsConstant.INTERACT_IMPL_ING);
		dueExecuteTaskDao.updateDueExecuteTaskStatus(SearchParamUtil.getSearchParam(dto));
	}
	
	/**
	 * 
	 * @author chenguixue
	 * @date 2015-3-27 下午2:50:02
	 * @see com.jy.modules.cims.component.sysAssist.AbstractDueExecuteTaskManager#runDueExecuteTask(java.lang.String)
	 */
	@Override
	public void runDueExecuteTask(String taskId) {
		
		DueExecuteTaskCfgDto dto = new DueExecuteTaskCfgDto();
		dto.setTaskid(taskId);
		List<DueExecuteTaskCfgDto> list = dueExecuteTaskDao.searchDueExecuteTaskCfg(SearchParamUtil.getSearchParam(dto));
		if(list == null || list.size() != 1) {
			throw new SysAssistException(CimsExceptionConstant.SYSASSIST_DUETASK_TASKID_NOT_FOUND);
		}
		
		DueExecuteTaskCfgDto dueExecuteTaskDto = list.get(0);
				
		if(!checkDueTaskExecuteCondition(dueExecuteTaskDto)) return;
		
		/**
		 * 在真正触发到期任务之前先将任务置为进行中状态
		 * 主要考虑事务的一致性，因为到期任务可能涉及到多个事务的情况
		 * 处于进行中状态的任务如果长时间未执行完成，则需要人工处理
		 */
		runDueExecuteTaskBefore(taskId);
		
		try {
			runDueExecuteTaskTrue(dueExecuteTaskDto);
			dueExecuteTaskDto.setDuestatus(CimsConstant.INTERACT_IMPL_TRUE);
			
		} catch(Exception e) {
			e.printStackTrace();
			logger.error("到期任务执行错误", e);
			dueExecuteTaskDto.setFailnum(dueExecuteTaskDto.getFailnum() + 1);
		}
		
		dueExecuteTaskDao.updateDueExecuteTaskCfg(SearchParamUtil.getSearchParam(dueExecuteTaskDto));
		
	}

	@Override
	protected List<DueExecuteTaskCfgDto> getNeedDueExecuteTaskList() {
		DueExecuteTaskCfgDto dto = new DueExecuteTaskCfgDto();
		dto.setDuestatus(CimsConstant.INTERACT_IMPL_FALSE);
		dto.setFailnum(1L); // 考虑到到期执行的任务可能包含调用外部接口的情况，如果外部接口调用失败的话，是不能够重复调用的，写死为只允许调用一次
		
		List<DueExecuteTaskCfgDto> list = dueExecuteTaskDao.getNeedDueExecuteTask(SearchParamUtil.getSearchParam(dto));
		return list;
	}
	
	private boolean checkDueTaskExecuteCondition(DueExecuteTaskCfgDto dto) {
		if(dto == null)
			return false;
		if(dto.getDuedate().compareTo(new java.sql.Timestamp(new Date().getTime())) > 0) {
			return false;
		}
		if(CimsConstant.INTERACT_IMPL_ING.equals(dto.getDuestatus()) || CimsConstant.INTERACT_IMPL_TRUE.equals(dto.getDuestatus()))
			return false;
		return true;
	}
	
}
